﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
using System.Data.OleDb;

/*
 * Sync
txtSelectedFolder
ckbGetDescFolder
btnSelectFolder
ckbEn
ckbJa
ckbCn
ckbAddLang
btnSync

* Excel
btnImportFromExcel
btnExportToExcel

* Translate
txtSelectedFolderT
btnSelectFolderT
cbxTranslateLang
lblTranslate
ckbEnT
ckbJaT
ckbCnT
lblTranslate2
btnTranslate

 */
namespace ResxManager
{
	delegate void LogCallback(string msg);

	public partial class Form1 : Form
	{
		static List<FileInfo> fileListS = null;
		static List<FileInfo> fileListT = null;
		static List<FileInfo> fileListE = null;
		#region constants


		public StringBuilder newResx = new StringBuilder(@"<?xml version='1.0' encoding='utf-8'?>
<root>
	<!-- 
		Microsoft ResX Schema

		Version 1.3

		The primary goals of this format is to allow a simple XML format 
		that is mostly human readable. The generation and parsing of the 
		various data types are done through the TypeConverter classes 
		associated with the data types.

		Example:

		... ado.net/XML headers & schema ...
		<resheader name='resmimetype'>text/microsoft-resx</resheader>
		<resheader name='version'>1.3</resheader>
		<resheader name='reader'>System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
		<resheader name='writer'>System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
		<data name='Name1'>this is my long string</data>
		<data name='Color1' type='System.Drawing.Color, System.Drawing'>Blue</data>
		<data name='Bitmap1' mimetype='application/x-microsoft.net.object.binary.base64'>
			[base64 mime encoded serialized .NET Framework object]
		</data>
		<data name='Icon1' type='System.Drawing.Icon, System.Drawing' mimetype='application/x-microsoft.net.object.bytearray.base64'>
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
		</data>

		There are any number of 'resheader' rows that contain simple 
		name/value pairs.

		Each data row contains a name, and value. The row also contains a 
		type or mimetype. Type corresponds to a .NET class that support 
		text/value conversion through the TypeConverter architecture. 
		Classes that don't support this are serialized and stored with the 
		mimetype set.

		The mimetype is used for serialized objects, and tells the 
		ResXResourceReader how to depersist the object. This is currently not 
		extensible. For a given mimetype the value must be set accordingly:

		Note - application/x-microsoft.net.object.binary.base64 is the format 
		that the ResXResourceWriter will generate, however the reader can 
		read any of the formats listed below.

		mimetype: application/x-microsoft.net.object.binary.base64
		value   : The object must be serialized with 
			: System.Serialization.Formatters.Binary.BinaryFormatter
			: and then encoded with base64 encoding.

		mimetype: application/x-microsoft.net.object.soap.base64
		value   : The object must be serialized with 
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
			: and then encoded with base64 encoding.

		mimetype: application/x-microsoft.net.object.bytearray.base64
		value   : The object must be serialized into a byte array 
			: using a System.ComponentModel.TypeConverter
			: and then encoded with base64 encoding.
	-->
	
	<xsd:schema id='root' xmlns='' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:msdata='urn:schemas-microsoft-com:xml-msdata'>
		<xsd:element name='root' msdata:IsDataSet='true'>
			<xsd:complexType>
				<xsd:choice maxOccurs='unbounded'>
					<xsd:element name='data'>
						<xsd:complexType>
							<xsd:sequence>
								<xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
								<xsd:element name='comment' type='xsd:string' minOccurs='0' msdata:Ordinal='2' />
							</xsd:sequence>
							<xsd:attribute name='name' type='xsd:string' msdata:Ordinal='1' />
							<xsd:attribute name='type' type='xsd:string' msdata:Ordinal='3' />
							<xsd:attribute name='mimetype' type='xsd:string' msdata:Ordinal='4' />
						</xsd:complexType>
					</xsd:element>
					<xsd:element name='resheader'>
						<xsd:complexType>
							<xsd:sequence>
								<xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
							</xsd:sequence>
							<xsd:attribute name='name' type='xsd:string' use='required' />
						</xsd:complexType>
					</xsd:element>
				</xsd:choice>
			</xsd:complexType>
		</xsd:element>
	</xsd:schema>
	<resheader name='resmimetype'>
		<value>text/microsoft-resx</value>
	</resheader>
	<resheader name='version'>
		<value>1.3</value>
	</resheader>
	<resheader name='reader'>
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
	</resheader>
	<resheader name='writer'>
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
	</resheader>
</root>");
		#endregion
		
		public Form1()
		{
			InitializeComponent();
		}

		#region Event Handlers
		private void button1_Click(object sender, EventArgs e)
		{
			using (FolderBrowserDialog fd = new FolderBrowserDialog())
			{
				fd.Description = "폴더 선택";
				fd.ShowNewFolderButton = true;
				fd.RootFolder = Environment.SpecialFolder.MyComputer;

				if (fd.ShowDialog() == DialogResult.OK)
				{
					fileListS = new List<FileInfo>();
					txtSelectedFolder.Text = fd.SelectedPath;
					GetFiles(fd.SelectedPath, fileListS, "ko", "s");
				}
			}
		}

		private void btnSelectFolderT_Click(object sender, EventArgs e)
		{
			using (FolderBrowserDialog fd = new FolderBrowserDialog())
			{
				fd.Description = "폴더 선택";
				fd.ShowNewFolderButton = false;
				fd.RootFolder = Environment.SpecialFolder.MyComputer;
				
				if(fd.ShowDialog() == DialogResult.OK)
				{
					fileListT = new List<FileInfo>();
					txtSelectedFolderT.Text = fd.SelectedPath;
					string lang = "";
					if(cbxTranslateLang.SelectedItem.ToString() == "한국어")
						lang = "ko";
					else if(cbxTranslateLang.SelectedItem.ToString() == "영어")
						lang = "en";
					GetFiles(fd.SelectedPath, fileListT, lang, "t");
				}
			}
		}

		private void btnSelectFolderE_Click(object sender, EventArgs e)
		{	
			using (FolderBrowserDialog fd = new FolderBrowserDialog())
			{
				fd.Description = "폴더 선택";
				fd.ShowNewFolderButton = false;
				fd.RootFolder = Environment.SpecialFolder.MyComputer;
				
				if(fd.ShowDialog() == DialogResult.OK)
				{
					fileListE = new List<FileInfo>();
					txtSelectedFolderE.Text = fd.SelectedPath;
					GetFiles(fd.SelectedPath, fileListE, "", "e");
				}
			}
		}
		
		private void cbxTranslateLang_SelectedIndexChanged(object sender, EventArgs e)
		{
			ckbEnT.Enabled = false;
			ckbJaT.Enabled = false;
			ckbCnT.Enabled = false;
			lblTranslate2.Enabled = false;
			btnTranslate.Enabled = false;
			txtSelectedFolderT.Text = "";
			fileListT = null;
		}

		private void btnClear_Click(object sender, EventArgs e)
		{
			txtLog.Clear();
		}

		private void btnSync_Click(object sender, EventArgs e)
		{
			foreach (FileInfo f in fileListS)
			{
				if (ckbEn.Checked)
					Sync(f, "en");
				if (ckbJa.Checked)
					Sync(f, "ja");
				if (ckbCn.Checked)
					Sync(f, "zh-cn");
			}
		}

		private void btnExportToExcel_Click(object sender, EventArgs e)
		{
			ToExcel();
			Log("----------Done!----------");
		}

		private void btnOpenFolderE_Click(object sender, EventArgs e)
		{
			System.Diagnostics.Process.Start("explorer.exe", txtSelectedFolderE.Text);
		}	
		
		private void btnInportFromExcel_Click(object sender, EventArgs e)
		{
			MessageBox.Show("엑셀에서 읽어온 내용을 기존 Resx파일에 덮어 씌우게 됩니다. 계속 진행하시겠습니까?", "엑셀 불러오기", MessageBoxButtons.YesNo);
			using (OpenFileDialog fd = new OpenFileDialog())
			{
				fd.FileName = "*.xls";
				if (fd.ShowDialog() == DialogResult.OK)
					ReadExcel(fd.FileName);
			}
		}

		private void btnTranslate_Click(object sender, EventArgs e)
		{
			MessageBox.Show("자동 번역 시 이전 파일들을 덮어씌우게 됩니다. 계속 진행하시겠습니까?", "자동 번역", MessageBoxButtons.YesNo);
			string from = "";
			if(cbxTranslateLang.SelectedItem.ToString() == "한국어")
				from = "ko";
			else if(cbxTranslateLang.SelectedItem.ToString() == "영어")
				from = "en";
			
			if (ckbEnT.Checked)
				Translate("en", from);
			if (ckbCnT.Checked)
				Translate("zh-Cn", from);
			if (ckbJaT.Checked)
				Translate("ja", from);
			Log("----------Done!----------");
		}
		#endregion
		
		
		private void GetFiles(string path, List<FileInfo> list, string lang, string type )
		{
			DirectoryInfo dir = new DirectoryInfo(path);
			if(type == "s")
			{
				if (ckbGetDescFolder.Checked)
				{
					foreach (FileInfo f in dir.GetFiles("*.resx", SearchOption.AllDirectories))
					{
						if (f.Name.ToLower().EndsWith(".zh-cn.resx") || f.Name.ToLower().EndsWith(".ja.resx") || f.Name.ToLower().EndsWith(".en.resx"))
							continue;
						list.Add(f);
						Log(f.DirectoryName + "\\" + f.Name);
					}
				}
				else
				{
					foreach (FileInfo f in dir.GetFiles("*.resx"))
					{
						if (f.Name.ToLower().EndsWith(".zh-cn.resx") || f.Name.ToLower().EndsWith(".ja.resx") || f.Name.ToLower().EndsWith(".en.resx"))
							continue;
						list.Add(f);
						Log(f.DirectoryName + "\\" + f.Name);
					}
				}
				if(list.Count > 0)
				{
					ckbEn.Enabled = true;
					ckbJa.Enabled = true;
					ckbCn.Enabled = true;
					btnSync.Enabled = true;
					ckbAddLang.Enabled = true;
				}
				
				Log("\n\n\rTotal " + list.Count + " Korean resx files found");
			}
			else if(type == "t")
			{
				if (lang == "ko")
				{
					foreach (FileInfo f in dir.GetFiles("*.resx"))
					{
						if (f.Name.ToLower().EndsWith(".zh-cn.resx") || f.Name.ToLower().EndsWith(".ja.resx") || f.Name.ToLower().EndsWith(".en.resx"))
							continue;
						list.Add(f);
						Log(f.DirectoryName + "\\" + f.Name);
					}
					Log("\n\n\rTotal " + list.Count + " Korean resx files found");
				}
				else if(lang == "en")
				{
					foreach (FileInfo f in dir.GetFiles("*.resx"))
					{
						if(f.Name.ToLower().EndsWith(".en.resx"))
						{
							list.Add(f);
							Log(f.DirectoryName + "\\" + f.Name);
						}
					}
					Log("\n\n\rTotal " + list.Count + " English resx files found");
				}
				if(list.Count > 0)
				{
					ckbEnT.Enabled = true;
					ckbJaT.Enabled = true;
					ckbCnT.Enabled = true;
					lblTranslate2.Enabled = true;
					btnTranslate.Enabled = true;
				}
			}
			else if(type == "e")
			{
				foreach (FileInfo f in dir.GetFiles("*.resx"))
				{
					if (f.Name.ToLower().EndsWith(".zh-cn.resx") || f.Name.ToLower().EndsWith(".ja.resx") || f.Name.ToLower().EndsWith(".en.resx"))
						continue;
					list.Add(f);
					Log(f.DirectoryName + "\\" + f.Name);
				}
				
				Log("\n\n\rTotal " + list.Count + " Korean resx files found");
				
				if(list.Count > 0)
				{
					btnExportToExcel.Enabled = true;
					btnOpenFolderE.Enabled = true;
				}
			}
			else
				return;			
		}
		
		public void Log(string msg)
		{
			if(this.txtLog.InvokeRequired)
			{
				LogCallback d = new LogCallback(Log);
				this.Invoke(d, new object[] {msg});
			}
			else
			{
				if(txtLog.Lines.Length >= 10000)
					txtLog.Clear();
				
				txtLog.AppendText(msg + "\r\n");
				txtLog.ScrollToCaret();
			}
		}

		private XmlDocument AddNode(string name, string value, XmlDocument xd)
		{
			XmlDocument xdTmp = new XmlDocument();
			xdTmp.LoadXml("<data name='" + name + "' xml:space='preserve'>" +
								"<value>" + value + "</value>" +
							"</data>");
			xd.SelectSingleNode("root").AppendChild( xd.ImportNode(xdTmp.GetElementsByTagName("data").Item(0), true) );
			return xd;
		}

		private void Sync(FileInfo f, string lang)
		{
			string fname = "";
			string[] arr = f.Name.Split('.');
			for (int i = 0; i < arr.Length - 1; i++)
			{
				fname += arr[i] + ".";
			}
			fname.Remove(fname.Length - 1);

			XmlDocument xdKo = new XmlDocument();
			xdKo.Load(f.DirectoryName + "\\" + f.Name);

			XmlDocument xdNew = new XmlDocument();
			if (File.Exists(f.DirectoryName + "\\" + fname + lang + ".resx"))
				xdNew.Load(f.DirectoryName + "\\" + fname + lang + ".resx");
			else
			{
				xdNew = xdKo;
				xdNew.Save(f.DirectoryName + "\\" + fname + lang + ".resx");
				Log("[kor->" + lang + "] " + f.DirectoryName + "\\" + fname + lang + ".resx Added.");
				return;
			}

			XmlNodeList xnlKo = xdKo.GetElementsByTagName("data");
			XmlNodeList xnlNew = xdNew.GetElementsByTagName("data");
			XmlNode xnTmp;

			bool flag;
			foreach (XmlNode x in xnlKo)
			{
				flag = false;
				foreach (XmlNode y in xnlNew)
				{
					if (x.Attributes["name"].Value == y.Attributes["name"].Value)
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					xnTmp = xdNew.ImportNode(x, true);
					if (ckbAddLang.Checked)
						xnTmp["value"].InnerText = "(" + lang + ")" + xnTmp["value"].InnerText;
					xdNew.SelectSingleNode("root").AppendChild(xnTmp);
					Log("[kor->" + lang + "] " + xdNew.ImportNode(x, true).Attributes["name"].Value + " Added.");
				}
			}
			xdNew.Save(f.DirectoryName + "\\" + fname + lang + ".resx");
			Log("[kor->" + lang + "] Done.\n\n");
		}

		public void ReadExcel(string name)
		{
			Excel e = new Excel();
			DataSet ds = e.OpenExcel(name, true);
			foreach (DataTable t in ds.Tables)
			{
				XmlDocument[] xdNew = new XmlDocument[t.Columns.Count - 1];
				string[] lang = new string[t.Columns.Count - 1];
				string[] dir = new string[t.Columns.Count - 1];
				t.PrimaryKey = new DataColumn[] { t.Columns["id"] };

				for (int i = 0; i < t.Columns.Count - 1; i++)
				{
					xdNew[i] = new XmlDocument();
					xdNew[i].LoadXml(newResx.ToString());
					lang[i] = t.Columns[i + 1].ColumnName;
					dir[i] = t.Rows.Find("[DIRECTORY]")[i + 1].ToString();
				}

				foreach (DataRow r in t.Rows)
					for (int i = 0; i < t.Columns.Count - 1; i++)
						if (!string.IsNullOrEmpty(dir[i]) && r[0].ToString() != "[DIRECTORY]")
							xdNew[i] = AddNode(r[0].ToString(), r[i + 1].ToString(), xdNew[i]);

				for (int i = 0; i < t.Columns.Count - 1; i++)
					if (!string.IsNullOrEmpty(dir[i]))
					{
						xdNew[i].Save(dir[i]);
						Log("[Saved] " + dir[i]);
					}
			}
		}

		public void ToExcel()
		{
			DataSet ds = null;
			try
			{
				ds = new DataSet();
				foreach (FileInfo f in fileListE)
				{
					string fname = "";
					string[] arr = f.Name.Split('.');
					for (int i = 0; i < arr.Length - 1; i++)
					{
						fname += arr[i] + ".";
					}
					fname.Remove(fname.Length - 1);

					DataTable dtKo = new DataTable();
					dtKo.TableName = f.Name.Replace(".resx", "");
					dtKo.Columns.Add("id", typeof(string));
					dtKo.Columns.Add("ko", typeof(string));
					dtKo.PrimaryKey = new DataColumn[] { dtKo.Columns["id"] };

					//한국어
					XmlDocument xdKo = new XmlDocument();
					xdKo.Load(f.DirectoryName + "\\" + f.Name);
					XmlNodeList xnlKo = xdKo.GetElementsByTagName("data");

					foreach (XmlNode xn in xnlKo)
					{
						DataRow dr = dtKo.NewRow();
						dr["id"] = xn.Attributes["name"].Value;
						dr["ko"] = xn.SelectSingleNode("value").InnerText;
						if (dtKo.Rows.Find(dr["id"]) == null)
							dtKo.Rows.Add(dr);
					}
					DataRow drDir = dtKo.NewRow();
					drDir["id"] = "[DIRECTORY]";
					drDir["ko"] = f.DirectoryName + "\\" + f.Name;
					dtKo.Rows.Add(drDir);

					//외국어
					string[] lang = { "en", "ja", "zh-cn" };
					foreach (string l in lang)
					{
						DataTable dtNew = new DataTable();
						dtNew.Columns.Add("id", typeof(string));
						dtNew.Columns.Add(l, typeof(string));
						dtNew.PrimaryKey = new DataColumn[] { dtNew.Columns["id"] };

						XmlDocument xdNew = new XmlDocument();
						if (File.Exists(f.DirectoryName + "\\" + fname + l + ".resx"))
							xdNew.Load(f.DirectoryName + "\\" + fname + l + ".resx");
						else
						{
							dtKo.Merge(dtNew);
							continue;
						}

						XmlNodeList xnlNew = xdNew.GetElementsByTagName("data");

						foreach (XmlNode xn in xnlNew)
						{
							if (dtNew.Rows.Find(xn.Attributes["name"].Value) != null)
								continue;
							DataRow dr = dtNew.NewRow();
							dr["id"] = xn.Attributes["name"].Value;
							dr[l] = xn.SelectSingleNode("value").InnerText;
							if (dtNew.Rows.Find(dr["id"]) == null)
								dtNew.Rows.Add(dr);
						}
						drDir = dtNew.NewRow();
						drDir["id"] = "[DIRECTORY]";
						drDir[l] = f.DirectoryName + "\\" + fname + l + ".resx";
						dtNew.Rows.Add(drDir);

						dtKo.Merge(dtNew);

					}
					ds.Tables.Add(dtKo);
					pgb.Value += 100 / fileListE.Count;
				}
				// 				if( SaveExcel(txtSelectedFolder.Text + "\\resx", ds, true, true) )
				// 					Log(txtSelectedFolder.Text + "\\resx.xls Saved.");
				Excel e = new Excel();
				if (e.SaveExcel("D:" + "\\resx.xls", ds, true, true))
					Log("D:" + "\\resx.xls Saved.");
				Log("Done");
				pgb.Value = 0;
			}
			catch (System.Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}


		public void Translate(string lang, string from)
		{
			foreach (FileInfo f in fileListT)
			{
				pgb.Value = 0;
				Log("[" + from + "->" + lang + "]" + f.FullName + " translating...");
				XmlDocument xd = new XmlDocument();
				xd.Load(f.FullName);
				if (xd == null)
				{
					Log("Failed to open " + f.FullName);
					return;
				}

				XmlDocument xdNew = new XmlDocument();
				xdNew.LoadXml(newResx.ToString());

				XmlNodeList xnl = xd.GetElementsByTagName("data");
				Translate t = new Translate();
				foreach (XmlNode x in xnl)
				{
					pgb.Value += 100 / xnl.Count;
					xdNew = AddNode(x.Attributes["name"].Value, t.TranslateText(x.SelectSingleNode("value").InnerText, from.ToLower(), lang.ToLower()), xdNew);
				}
				if (from == "ko")
					xdNew.Save(f.DirectoryName + "\\" + f.Name.Substring(0, f.Name.LastIndexOf(".resx")) + "." + lang + ".resx");
				else
					xdNew.Save(f.DirectoryName + "\\" + f.Name.Substring(0, f.Name.LastIndexOf("." + from + ".")) + "." + lang + ".resx");
				Log("[" + from + "->" + lang + "]" + f.FullName + " translated");
			}
		}
	}
}
